<?php
/**
 * 服务层基类 - ThinkPHP 8 优化版
 *
 * @author Claude Code
 * @since 2025-11-03
 */

namespace app\service;

use think\facade\Cache;
use think\facade\Db;
use think\Model;

/**
 * 服务层基类
 *
 * 提供通用的业务逻辑处理方法
 *
 * 优化特性：
 * 1. 统一缓存管理
 * 2. 自动事务处理
 * 3. 数据验证增强
 * 4. 日志记录
 * 5. 分页优化
 * 6. 批量操作支持
 */
abstract class BaseService
{
    /**
     * 模型实例
     * @var Model
     */
    protected $model;

    /**
     * 缓存键前缀
     * @var string
     */
    protected $cachePrefix = '';

    /**
     * 缓存过期时间（秒）
     * @var int
     */
    protected $cacheExpire = 3600;

    /**
     * 是否启用缓存
     * @var bool
     */
    protected $cacheEnabled = false;

    /**
     * 数据库连接标识
     * @var string
     */
    protected $connection = '';

    /**
     * 日志通道
     * @var string
     */
    protected $logChannel = 'business';

    /**
     * 构造函数
     */
    public function __construct()
    {
        $this->initialize();
    }

    /**
     * 初始化
     */
    protected function initialize()
    {
        // 子类可重写此方法进行初始化
    }

    /**
     * 设置模型
     * @param Model $model
     * @return $this
     */
    protected function setModel(Model $model)
    {
        $this->model = $model;
        return $this;
    }

    /**
     * 获取模型实例
     * @return Model
     */
    protected function getModel(): Model
    {
        if (!$this->model) {
            throw new \Exception('模型未初始化');
        }
        return $this->model;
    }

    /**
     * 设置缓存配置
     * @param bool $enabled 是否启用缓存
     * @param string $prefix 缓存键前缀
     * @param int $expire 过期时间（秒）
     * @return $this
     */
    protected function setCacheConfig(bool $enabled = true, string $prefix = '', int $expire = 3600)
    {
        $this->cacheEnabled = $enabled;
        $this->cachePrefix = $prefix;
        $this->cacheExpire = $expire;
        return $this;
    }

    /**
     * 生成缓存键
     * @param string $key 缓存键
     * @return string
     */
    protected function getCacheKey(string $key): string
    {
        return $this->cachePrefix . $key;
    }

    /**
     * 获取缓存
     * @param string $key 缓存键
     * @param mixed $default 默认值
     * @return mixed
     */
    protected function getCache(string $key, $default = null)
    {
        if (!$this->cacheEnabled) {
            return $default;
        }

        $cacheKey = $this->getCacheKey($key);
        return Cache::get($cacheKey, $default);
    }

    /**
     * 设置缓存
     * @param string $key 缓存键
     * @param mixed $value 缓存值
     * @param int|null $expire 过期时间
     * @return bool
     */
    protected function setCache(string $key, $value, ?int $expire = null): bool
    {
        if (!$this->cacheEnabled) {
            return false;
        }

        $cacheKey = $this->getCacheKey($key);
        $expire = $expire ?? $this->cacheExpire;
        return Cache::set($cacheKey, $value, $expire);
    }

    /**
     * 删除缓存
     * @param string $key 缓存键
     * @return bool
     */
    protected function deleteCache(string $key): bool
    {
        if (!$this->cacheEnabled) {
            return false;
        }

        $cacheKey = $this->getCacheKey($key);
        return Cache::delete($cacheKey);
    }

    /**
     * 清空缓存
     * @param string|null $tag 缓存标签
     * @return bool
     */
    protected function clearCache(?string $tag = null): bool
    {
        if (!$this->cacheEnabled) {
            return false;
        }

        if ($tag) {
            return Cache::tag($tag)->clear();
        }

        return Cache::clear();
    }

    /**
     * 记录日志
     * @param string $level 日志级别
     * @param string $message 消息
     * @param array $context 上下文
     * @return void
     */
    protected function log(string $level, string $message, array $context = []): void
    {
        $data = [
            'service' => static::class,
            'timestamp' => time(),
            'context' => $context
        ];

        log($level, $message, $data);
    }

    /**
     * 信息日志
     * @param string $message
     * @param array $context
     * @return void
     */
    protected function logInfo(string $message, array $context = []): void
    {
        $this->log('info', $message, $context);
    }

    /**
     * 警告日志
     * @param string $message
     * @param array $context
     * @return void
     */
    protected function logWarning(string $message, array $context = []): void
    {
        $this->log('warning', $message, $context);
    }

    /**
     * 错误日志
     * @param string $message
     * @param array $context
     * @return void
     */
    protected function logError(string $message, array $context = []): void
    {
        $this->log('error', $message, $context);
    }

    /**
     * 执行事务
     * @param callable $callback 回调函数
     * @param int $ Attempts 重试次数
     * @return mixed
     * @throws \Exception
     */
    protected function transaction(callable $callback, int $attempts = 1)
    {
        $lastException = null;

        for ($attempt = 1; $attempt <= $attempts; $attempt++) {
            try {
                Db::startTrans();
                $result = $callback($this);
                Db::commit();
                return $result;
            } catch (\Exception $e) {
                Db::rollback();
                $lastException = $e;

                $this->logError("事务执行失败 (尝试 {$attempt}/{$attempts})", [
                    'message' => $e->getMessage(),
                    'file' => $e->getFile(),
                    'line' => $e->getLine()
                ]);

                if ($attempt >= $attempts) {
                    break;
                }

                // 等待一段时间后重试
                usleep(100000 * $attempt); // 0.1s * 尝试次数
            }
        }

        throw $lastException;
    }

    /**
     * 数据验证
     * @param array $data 待验证数据
     * @param array|string $validate 验证器
     * @param array $message 错误消息
     * @param bool $batch 是否批量验证
     * @return bool
     * @throws ValidateException
     */
    protected function validateData(array $data, $validate, array $message = [], bool $batch = true): bool
    {
        if (is_array($validate)) {
            $v = new \think\Validate();
            $v->message($message)->batch($batch);
            if (!$v->check($data, $validate)) {
                throw new ValidateException($v->getError());
            }
            return true;
        }

        if (is_string($validate)) {
            $v = new $validate();
            if (!$v->message($message)->batch($batch)->check($data)) {
                throw new ValidateException($v->getError());
            }
            return true;
        }

        return false;
    }

    /**
     * 批量插入
     * @param array $dataList 数据列表
     * @param int $chunk 批量大小
     * @return int 插入数量
     */
    protected function batchInsert(array $dataList, int $chunk = 1000): int
    {
        $count = 0;
        $chunks = array_chunk($dataList, $chunk);

        foreach ($chunks as $chunkData) {
            $result = $this->getModel()->insertAll($chunkData);
            $count += $result;
        }

        $this->logInfo("批量插入完成", [
            'total' => count($dataList),
            'chunks' => count($chunks)
        ]);

        return $count;
    }

    /**
     * 批量更新
     * @param array $dataList 数据列表
     * @param string $primaryKey 主键字段名
     * @param int $chunk 批量大小
     * @return int 更新数量
     */
    protected function batchUpdate(array $dataList, string $primaryKey = 'id', int $chunk = 1000): int
    {
        $count = 0;
        $chunks = array_chunk($dataList, $chunk);

        foreach ($chunks as $chunkData) {
            foreach ($chunkData as $data) {
                if (!isset($data[$primaryKey])) {
                    continue;
                }

                $id = $data[$primaryKey];
                unset($data[$primaryKey]);

                $result = $this->getModel()->where($primaryKey, $id)->update($data);
                if ($result) {
                    $count++;
                }
            }
        }

        $this->logInfo("批量更新完成", [
            'total' => count($dataList),
            'chunks' => count($chunks)
        ]);

        return $count;
    }

    /**
     * 分页查询
     * @param array $params 查询参数
     * @param int $page 页码
     * @param int $limit 每页数量
     * @return array
     */
    protected function paginate(array $params = [], int $page = 1, int $limit = 15): array
    {
        $model = $this->getModel();

        // 应用查询条件
        if (!empty($params)) {
            $model = $model->where($params);
        }

        // 获取总数
        $total = $model->count();

        // 获取分页数据
        $list = $model->page($page, $limit)
            ->select()
            ->toArray();

        return [
            'list' => $list,
            'total' => $total,
            'page' => $page,
            'limit' => $limit,
            'pages' => ceil($total / $limit)
        ];
    }

    /**
     * 获取分页数据（带缓存）
     * @param array $params 查询参数
     * @param int $page 页码
     * @param int $limit 每页数量
     * @param string $cacheKey 缓存键
     * @return array
     */
    protected function paginateWithCache(array $params = [], int $page = 1, int $limit = 15, string $cacheKey = ''): array
    {
        if (!$this->cacheEnabled || !$cacheKey) {
            return $this->paginate($params, $page, $limit);
        }

        // 生成缓存键
        $key = $cacheKey . ':' . md5(serialize($params) . $page . $limit);

        // 尝试从缓存获取
        $cached = $this->getCache($key);
        if ($cached !== null) {
            return $cached;
        }

        // 查询数据
        $result = $this->paginate($params, $page, $limit);

        // 设置缓存
        $this->setCache($key, $result);

        return $result;
    }

    /**
     * 软删除
     * @param int|array $id 主键ID或ID数组
     * @return bool
     */
    protected function softDelete($id): bool
    {
        return $this->getModel()->destroy($id) > 0;
    }

    /**
     * 恢复软删除
     * @param int|array $id 主键ID或ID数组
     * @return bool
     */
    protected function restore($id): bool
    {
        $model = $this->getModel();
        if (method_exists($model, 'restore')) {
            return $model->restore($id) > 0;
        }

        // 手动恢复
        $updateData = [
            $model->getDeleteTimeField() => null,
            'update_time' => time()
        ];

        if (is_array($id)) {
            return $model->where('id', 'in', $id)->update($updateData) > 0;
        } else {
            return $model->where('id', $id)->update($updateData) > 0;
        }
    }

    /**
     * 彻底删除
     * @param int|array $id 主键ID或ID数组
     * @return bool
     */
    protected function forceDelete($id): bool
    {
        $model = $this->getModel();
        if (method_exists($model, 'forceDelete')) {
            return $model->forceDelete($id) > 0;
        }

        // 手动彻底删除
        if (is_array($id)) {
            return $model->where('id', 'in', $id)->delete() > 0;
        } else {
            return $model->where('id', $id)->delete() > 0;
        }
    }
}
